﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Net;
using System.Net.Sockets;

namespace SetNet
{
    abstract public class Server
    {
        //for UDP and TCP
        Socket _socket;
        IPEndPoint localEndPoint;
        byte[] buffer;
        byte[] check;
        //for UDP
        static List<PeerBase> clients = new List<PeerBase>();
        byte[] check_buffer = new byte[7];
        IPEndPoint point = new IPEndPoint(IPAddress.Any, 0);
        EndPoint ePoint = null;
        
        public Server()
        {
            Start(Configuration.port);
        }

        void Start(int port)
        {
            IPAddress ipAddress = Configuration.adress;
            Console.WriteLine("Binding server on: {0}:{1}",Configuration.adress,Configuration.port);
            localEndPoint = new IPEndPoint(ipAddress, Configuration.port);

            //TCP Receive
            if (Configuration.type == (byte)Configuration.socketType.tcp)
            {
                _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                Console.WriteLine("Main Socket is created.");
                _socket.Bind(localEndPoint);
                _socket.Listen(Configuration.maxClients);
                Console.WriteLine("Begin accept TCP client...");
            }

            //UDP Receive
            else if (Configuration.type == (byte)Configuration.socketType.udp)
            {
                buffer = new byte[Configuration.bufferLenght];
                check = new byte[Configuration.bufferLenght];
                check = Encoding.UTF8.GetBytes("TimeOUT");
                ePoint = (EndPoint)point;
                
                _socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
                Console.WriteLine("Main Socket is created.");
                Console.WriteLine("Begin accept UDP client...");
                _socket.Bind(localEndPoint);
            }

            //Start Thread for receive and Accept
            Thread acceptTcp = new Thread(Accept);
            acceptTcp.IsBackground = true;
            acceptTcp.Start();
        }


        //Add PeerBase to clients. For UDP


        abstract public void NewClient(ClientInfo info);

        void Accept()
        {
            if (Configuration.type == (byte)Configuration.socketType.tcp)
            {
                while (true)
                {
                    ClientInfo info = new ClientInfo();
                    Socket client = _socket.Accept();
                    info.socket = client;
                    info.endPoint = (IPEndPoint)client.RemoteEndPoint;
                    Console.WriteLine("[" + DateTime.Now.ToString() + "]: " + "Client connected.");
                    NewClient(info);

                }
            }
            else if (Configuration.type == (byte)Configuration.socketType.udp)
            {
                while (true)
                {
                    int recv = 0;
                    try
                    {
                        recv = _socket.ReceiveFrom(buffer, ref ePoint);
                        CheckingData(recv);
                    }
                    catch (SocketException ex)
                    {
                    }
                }

            }
        }

        public void SendUDP(byte[] data,EndPoint point)
        {
            try
            {
                _socket.SendTo(data, point);
                
            }
            catch(SocketException ex)
            {

            }
        }


        void CheckingData(int recv)
        {
            if (recv > 0 && recv <= 4)
            {
                ClientInfo info = new ClientInfo();
                info.socket = _socket;
                info.endPoint = ePoint;
                info.server = this;
                
                NewClient(info);
            }
            else if (recv > 4)
            {


                for (int i = 0; i < 7; i++)
                {
                    check_buffer[i] = buffer[i];
                }

                if (!check_buffer.SequenceEqual(check))
                {
                    foreach (PeerBase peer in clients)
                    {
                        if (ePoint.Equals(peer.ePoint))
                        {
                            EventData data = Serialization.Deserializate(buffer);
                            peer.ReceiveData(data);
                        }
                    }
                }

                else
                {
                    for (int i = 0; i < clients.Count; i++)
                    {
                        if (ePoint.Equals(clients[i].ePoint))
                        {
                            clients[i].UpdTimeOutChecker();
                        }
                    }
                }
            }
        }



        public static void AddClient(PeerBase addObject)
        {
            if (clients.Contains(addObject))
            {
                return;
            }
            else
            {
                clients.Add(addObject);
                Console.WriteLine("[" + DateTime.Now.ToString() + "]: UDP Client Count = " + clients.Count);
            }
        }
        public void Removeclient(PeerBase peer)
        {
            clients.Remove(peer);
        }
    }
}
